package com.edu.gateway.filter;


import com.edu.gateway.base.GateWayResultCode;
import com.edu.gateway.base.GatewayHelper;
import com.edu.gateway.base.R;
import com.edu.gateway.utils.EncryptUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Date;

/**
 * X-Ca-Key   签名的key  每个版本key都不一样
 * X-Ca-Timestamp   调用方生成的时间戳 3分钟时间差。超过会报错
 * X-Ca-Nonce   随机数，最好雪花算法生成，一个请求一个
 * X-Ca-Content-Signature  md5 加密请求体
 * X-Ca-Signature    通过对 (content_type, key, timestamp,ca-id , token, url )   进行 加密后的数据
 * 加密方式  EncryptUtil.encodeByHmacSHA256(origData,secure);
 */
@Slf4j
@Component
public class SafetyAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {

    public static String X_CA_KEY="X-Ca-Key";
    public static String X_CA_TIMESTAMP="X-Ca-Timestamp";
    // 传 1
    public static String X_CA_VERSION="X-Ca-Version";
    public static String X_CA_SIGNATURE="X-Ca-Signature";
    // 未启用 sha1 sha256 md5
    public static String X_CA_SIGNATURE_METHOD="X-Ca-Signature-Method";
    // 随机数，最好雪花算法生成，一个请求一个
    public static final String X_CA_NOCE=  "X-Ca-Nonce";
    // 内容摘要 现在不做内容签名验证，有值就行
    public static final String X_CA_CONTENT_SIGNATURE="X-Ca-Content-Signature" ;

    @Autowired
    private Environment environment;

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            log.info("gateway filter uri:{}",exchange.getRequest().getPath());
            return filter(exchange,chain);
        };
    }

    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        try {
            String path = exchange.getRequest().getPath().value();
            log.info("SafetyAuthGatewayFilter url:{} ,path: {}",exchange.getRequest().getURI(),path);

            //timestamp 合规
            String timestamp=exchange.getRequest().getHeaders().getFirst(X_CA_TIMESTAMP);
            if(StringUtils.isBlank(timestamp)||!checkTimestamp(timestamp)){
                log.info("SafetyAuthGatewayFilter checktime fail:{}",timestamp);
                return GatewayHelper.fail(exchange, GateWayResultCode.SAFETY_TIMESTAMP);
            }
            String version = exchange.getRequest().getHeaders().getFirst(X_CA_VERSION);
            if(StringUtils.isBlank(version)){
                log.info("SafetyAuthGatewayFilter ca_version fail:{}",version);
                return GatewayHelper.fail(exchange, GateWayResultCode.SAFETY_VERSION);
            }
            R<String> ssr = getSign(exchange);
            if(! ssr.isSuccess()){
                return GatewayHelper.fail(exchange, GateWayResultCode.SAFETY_CAKEY);
            }
            String sign = ssr.getData();
            log.info("SafetyAuthGatewayFilter sign string:{}",sign);
            String key=exchange.getRequest().getHeaders().getFirst(X_CA_KEY);
            String paramsign=exchange.getRequest().getHeaders().getFirst(X_CA_SIGNATURE);
            // 计算 sign签名
            R<String> sr= getSignBykey(key, sign);
            if(!sr.isSuccess()){
                return GatewayHelper.fail(exchange, GateWayResultCode.SAFETY_BUILD_FAIL);
            }
            String signBykey = sr.getData();
            if(!paramsign.equals(signBykey)){
                log.info("SafetyAuthGatewayFilter 计算的sign:{} , 前端传递的sign :{}",signBykey,paramsign);
                return GatewayHelper.fail(exchange, GateWayResultCode.SAFETY_CAKEY_SIGN);
            }
            return chain.filter(exchange);
        } catch (Exception e) {
            log.error("SafetyAuthGatewayFilter 鉴权未知异常",e);
            return GatewayHelper.fail(exchange, GateWayResultCode.SAFETY_ERROR);
        }
    }

    /**
     * @param timestamp
     * @return 时间验证结果
     */
    private boolean checkTimestamp(String timestamp) {
        Date date = new Date();
        long time = date.getTime();
        //10分钟毫秒
        long add =600000;
        long after=time+add;
        long begin=time-add;
        Long catime = Long.valueOf(timestamp);
        log.info("checkTimestamp time:{}  after:{}  begin:{} timestamp:{} catime:{}",time,after,begin,timestamp,catime);
        if(catime >begin && catime<after){
            return true;
        }
        return false;
    }

    /**
     * 根据 key 获取加密后的数据
     * @param appKey
     * @param signStr
     * @return
     * @throws Exception
     */
    public  R  getSignBykey(String appKey,String signStr) throws Exception {
        // 密钥
        String sec = environment.getProperty("safety."+appKey+".secretkey");
        // 加密方式
        String type = environment.getProperty("safety."+appKey+".enctype");
        //验证 密钥和 加密方式是否合法
        if(StringUtils.isBlank(sec) && StringUtils.isBlank(type)){
            log.error("获取签名密钥失败 appKey:{}",appKey);
            return R.fail("获取签名密钥失败"+appKey);
        }
        if(EncryptUtil.SHA256.equals(type)){
            String sign= EncryptUtil.encodeByHmacSHA256(signStr, sec);
            return R.ok(sign);
        }
        return R.fail("签名验证错误 没有对应的加密方法 key:"+appKey+"  type:"+type);
    }

    /**
     * @param exchange
     * @return 用于签名的字符串
     */
    private R getSign(ServerWebExchange exchange){
        String key=exchange.getRequest().getHeaders().getFirst(X_CA_KEY);
        if(StringUtils.isBlank(key)){
            R.fail("X_CA_KEY 为空");
        }
        String nonce=exchange.getRequest().getHeaders().getFirst(X_CA_NOCE);
        if(StringUtils.isBlank(nonce)){
            R.fail("X_CA_NOCE 为空");
        }
        String timestamp=exchange.getRequest().getHeaders().getFirst(X_CA_TIMESTAMP);
        if(StringUtils.isBlank(timestamp)){
            R.fail("X_CA_TIMESTAMP 为空");
        }
        String contentSign=exchange.getRequest().getHeaders().getFirst(X_CA_CONTENT_SIGNATURE);
        if(StringUtils.isBlank(contentSign)){
            R.fail("X_CA_CONTENT_SIGNATURE 为空");
        }
        String url = exchange.getRequest().getPath().value();

        log.info("SafetyAuthGatewayFilter key:{} ,nonce:{} ,timestamp:{},conteSign:{},url:{}",key,nonce,timestamp,contentSign,url);
        String s = buildStringToSign(key, timestamp, nonce, contentSign, url);
        return R.ok(s);
    }

    public static String buildStringToSign(String key,String timestamp,String nonce,String contentSign,String url) {
        String sign = String.format("%s:%s\n%s:%s\n%s:%s\n%s:%s\n%s",
                "X-Ca-Key",
                key,
                "X-Ca-Timestamp",
                timestamp,
                "X-Ca-Nonce",
                nonce,
                "X-Ca-Content-Signature",
                contentSign,
                url
        );
        return sign;
    }

}
